/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.scribeplugin;

import java.awt.Color;
import java.awt.Component;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Formatter;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.pearreview.ui.StepTabs;
import com.pearreview.ui.TipButton;
import com.pearreview.ui.clock.ScribeClock;
import com.pearreview.application.Messages;
import com.pearreview.application.PluginAdapter;
import com.pearreview.application.SaveData;
import com.pearreview.util.XMLFileFilter;
import com.pearreview.util.review.Finding;
import com.pearreview.util.review.Meeting;
import com.pearreview.util.review.Protocol;
import com.pearreview.util.review.Review;
import com.pearreview.util.review.io.ReviewReader;
import com.pearreview.util.review.io.ReviewWriter;
import com.pearreview.util.review.io.XMLReviewReader;
import com.pearreview.util.review.io.XMLReviewWriter;

public class ScribeView implements ScribeReviewFormValidator, Serializable {
	private static final long serialVersionUID = 4733953845319554018L;
	
	private static final String XML_PREFIX = "PearReview";
	
	public static final String FILE_HISTORY_TYPE = "review-xml";
	
	private JSplitPane splitPane;
	private StepTabs stepTabs;
	private String[] steps = {
			Messages.getString("PearReview.Scribe.tabs.start"),
		    Messages.getString("PearReview.Scribe.tabs.meetings"),
			Messages.getString("PearReview.Scribe.tabs.information"),
			Messages.getString("PearReview.Scribe.tabs.attendees"),
			Messages.getString("PearReview.Scribe.tabs.findings"),
			Messages.getString("PearReview.Scribe.tabs.export")};
	
	private PluginAdapter pluginAdapter;
	
	private File reviewFile;
	private Review review;
	private Meeting meeting;
	
	/* actions */
	private NextViewAction nextViewAction = new NextViewAction();
	private PreviousViewAction previousViewAction = new PreviousViewAction();
	private FirstViewAction firstViewAction = new FirstViewAction();
	private Action openReviewAction = new OpenReviewAction();
	private Action openReviewFileAction = new OpenReviewFileAction();
	private Action saveReviewAction = new SaveReviewAction();
	private Action saveAsReviewAction = new SaveAsReviewAction();
	private Action navigateToHomeAction = new NavigateToHomeAction();
	
	private JScrollPane mainScrollPane = new JScrollPane();
	private ScribeClock clock = new ScribeClock();
	
	private StartPanel startPanel;
	private MeetingsPanel meetingsPanel;
	private InformationPanel informationPanel;
	private AttendeePanel attendeePanel;
	private FindingsPanel findingsPanel;
	private ExportPanel exportPanel;
	
	private TipButton hintBtn;
	private int currentView = -1;
	
	/**
	 * If the user has to choose for open a review or a new review.
	 * Before choosing one of this two options, no next view available.
	 */
	private boolean startMode = true;
	
	private Component[] views;

	private boolean meetingRunning;
	
	/**
	 * Creates new scribe view
	 */
	public ScribeView(PluginAdapter pluginAdapter) {
		this.pluginAdapter = pluginAdapter;
		initComponents();
		this.setStartMode(true);
	}
	
	/**
	 * Returns default review file.
	 * @return default review file
	 */
	public static File getDefaultSaveAsFile() {
		return new File("review_" + 
				new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
	}
	
	/**
	 * Returns localized string of this component
	 * @param key key of string to localize
	 * @return localized string of this component
	 */
	private static String getLocalizedString(String key) {
		return Messages.getString("PearReview.Scribe." + key);
	}
	
	public Action getNextViewAction() {
		return this.nextViewAction;
	}
	
	public Action getPreviousViewAction() {
		return this.previousViewAction;
	}

	public Action getFirstViewAction() {
		return this.firstViewAction;
	}
	
	public Action getOpenReviewAction() {
		return openReviewAction;
	}
	
	public Action getSaveReviewAction() {
		return saveReviewAction;
	}
	
	public Action getSaveAsReviewAction() {
		return saveAsReviewAction;
	}
	
	public Action getNavigateToHomeAction() {
		return navigateToHomeAction;
	}
	
	/**
	 * Sets the button to show hints for current panel. 
	 * 
	 * @param hintBtn
	 */
	public void setTipBtn(TipButton hintBtn) {
		this.hintBtn = hintBtn;
		
		int index = stepTabs.getSelectedIndex();
		index = Math.max(index, 0);
		updateTip(index);
	}
	
	/**
	 * Sets the hint from current panel to the hint button.
	 */
	private void updateTip(int index) {
		if (hintBtn != null) {
			ScribePanel panel = (ScribePanel)views[index];
			hintBtn.setTipText(panel.getHint());
		}
	}
	
	/**
	 * Returns action for start step
	 * @param startMode
	 */
	private void setStartMode(boolean startMode) {
		this.startMode = startMode;
		if (startMode) {
			stepTabs.setMaxSelectionIndex(0);
		} else {
			stepTabs.setMaxSelectionIndex(1);
		}
		updateActions();
	}
	
	private void updateActions() {
		int selected = stepTabs.getSelectedIndex();
		int max = Math.max(stepTabs.getMaxSelectionIndex(), views.length - 1);
		nextViewAction.setEnabled(selected < max);
		
		if (selected == 0) {
			previousViewAction.setEnabled(false);
			firstViewAction.setEnabled(false);
		} else {
			previousViewAction.setEnabled(true);
			firstViewAction.setEnabled(true);
		}
	}
	
	public void setView(int index) {
		stepTabs.setSelectedIndex(index);
	}
	
	public int getSizeView() {
		return this.views.length;
	}
	
	public void setNextView() {
		stepTabs.setSelectedIndex(stepTabs.getSelectedIndex() + 1);
	}
	
	public void setPreviousView() {
		stepTabs.setSelectedIndex(stepTabs.getSelectedIndex() - 1);
	}
	
	public Component getPanel() {
		return mainScrollPane;
	}
	
	/**
	 * Marks the last tab as checked or with an error.
	 * 
	 * @param index
	 */
	private void checkTab(int index) {
		if(index < 0)
			return;
		
		ScribePanel panel = (ScribePanel)views[index];
		
		if (panel.isValidForm()) {
			stepTabs.setTabChecked(index);
			stepTabs.setToolTipTab(null, index);
		} else {
			stepTabs.setTabError(index);
			stepTabs.setToolTipTab(panel.getErrorMessage(), index);
		}
	}
	
	private void updateView() {
		if(currentView == stepTabs.getSelectedIndex())
			return;
		
		int divederLoc;
		//set only first time and save then the position
		//after tab changing
		if (splitPane.getDividerLocation() == -1) {
			divederLoc = 180;
		} else {
			divederLoc = splitPane.getDividerLocation();
		}
		
		int index = stepTabs.getSelectedIndex();
		index = Math.max(index, 0);
		if (currentView != index)
			checkTab(currentView);
		
		
		splitPane.setRightComponent(views[index]);
		splitPane.setDividerLocation(divederLoc);
		
		//set clock
		ScribePanel panel = (ScribePanel)views[index];
		panel.setClock(clock);
		
		//set hint
		updateTip(index);
		
		updateActions();
		
		if(views[index] == exportPanel) {
			exportPanel.setReview(getReview());
			exportPanel.setMeeting(meeting);
		}
		
		if(currentView >= 0) {
			int fontSize = ((ScribePanel)views[currentView]).getFontSize();
			if(fontSize >= 0)
				((ScribePanel)views[index]).setFontSize(fontSize);
		}
		
		currentView = index;
	}
	
	private void initComponents() {
		startPanel = new StartPanel(pluginAdapter,
				openReviewAction, openReviewFileAction);
		
		views = new Component[]{startPanel};
		
		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		splitPane.setContinuousLayout(true);
		splitPane.setBackground(new Color(0xd6d6d6));
		splitPane.setOneTouchExpandable(true);
		splitPane.setBorder(null);
		
		stepTabs = new StepTabs(steps);
		stepTabs.addListSelectionListener(new TabListener());
		stepTabs.setSelectedIndex(0);
		stepTabs.setMaxSelectionIndex(0);
		
		splitPane.setLeftComponent(stepTabs);
		
		mainScrollPane.setViewportView(splitPane);
		mainScrollPane.setBorder(null);
		updateView();
	}
	
	/**
	 * loads save data
	 * @param sd save data to load
	 */
	public void loadSaveData(SaveData sd) {
		if(sd instanceof ScribePluginSaveData) {
			ScribePluginSaveData spsd = (ScribePluginSaveData)sd;
			
			reviewFile = (File) spsd.getValue(
					ScribePluginSaveData.REVIEW_FILE_KEY);
			Review review = (Review) spsd.getValue(
					ScribePluginSaveData.REVIEW_KEY);
			Integer view = (Integer) spsd.getValue(
					ScribePluginSaveData.VIEW_KEY);
			Integer[] states = (Integer[]) spsd.getValue(
					ScribePluginSaveData.VIEW_STATES_KEY);
			Meeting meeting = (Meeting) spsd.getValue(
					ScribePluginSaveData.MEETING_KEY);
			Long clockPassedTime = (Long) spsd.getValue(
					ScribePluginSaveData.CLOCK_PASSED_TIME_KEY);
			Boolean clockRunning = (Boolean) spsd.getValue(
					ScribePluginSaveData.CLOCK_RUNNING_KEY);
			Boolean meetingRunning = (Boolean) spsd.getValue(
					ScribePluginSaveData.MEETING_RUNNING_KEY);
			File expFile = (File) spsd.getValue(
					ScribePluginSaveData.EXPORT_FILE_KEY);
			Boolean expSignatur = (Boolean) spsd.getValue(
					ScribePluginSaveData.EXPORT_SIGNATURE_KEY);
			Boolean expMeetings = (Boolean) spsd.getValue(
					ScribePluginSaveData.EXPORT_MEETINGS_KEY);
			Boolean expImages = (Boolean) spsd.getValue(
					ScribePluginSaveData.EXPORT_IMAGES_KEY);
			
			if(review != null) {
				setReview(review);
				setStartMode(false);
				pluginAdapter.setTitle(reviewFile.getName(), 
						reviewFile.getAbsolutePath());
				pluginAdapter.setFile(reviewFile);
				
				if(meeting != null) {
					this.meetingRunning = meetingRunning;
					setMeeting(meeting);
					
					if(meetingRunning) {
						Protocol protocol = meeting.getProtocol();
						protocol.setEnd(null);
						protocol.setDate(null);
						protocol.setStart(null);
						
						clock.setPassedTime(clockPassedTime);
						if(clockRunning) {
							clock.startTimer();
						}
					}
					meetingsPanel.setSelectedMeeting(meeting);
					meetingsPanel.setHighlightMeeting(meeting);
				}
			}
			
			setView(view);
			
			stepTabs.setStates(states);
			for(int index = 0 ; index < states.length ; index++) {
				if(states[index] != StepTabs.UNCHECKED_STATE) {
					checkTab(index);
				}
			}
			
			if(exportPanel != null) {
				if(expFile != null) {
					exportPanel.setLastExportFile(expFile);
				}
				if(expSignatur != null) {
					exportPanel.setCreateSignatureFieldsSelected(expSignatur);
				}
				if(expMeetings != null) {
					exportPanel.setExportOfAllMeetingsSelected(expMeetings);
				}
				if(expImages != null) {
					exportPanel.setPrintImagesSelected(expImages);
				}
			}
		}
	}
	
	/**
	 * Returns save data
	 * @return save data
	 */
	public ScribePluginSaveData getSaveData() {
		ScribePluginSaveData sd = new ScribePluginSaveData();
		sd.putValue(ScribePluginSaveData.REVIEW_KEY, 
				getReview());
		sd.putValue(ScribePluginSaveData.MEETING_KEY, 
				meeting);
		sd.putValue(ScribePluginSaveData.REVIEW_FILE_KEY, 
				reviewFile);
		sd.putValue(ScribePluginSaveData.VIEW_KEY, 
				stepTabs.getSelectedIndex());
		sd.putValue(ScribePluginSaveData.VIEW_STATES_KEY, 
				stepTabs.getStates());
		sd.putValue(ScribePluginSaveData.MEETING_RUNNING_KEY, 
				meetingRunning);
		sd.putValue(ScribePluginSaveData.CLOCK_PASSED_TIME_KEY, 
				clock.getPassedTime());
		sd.putValue(ScribePluginSaveData.CLOCK_RUNNING_KEY, 
				clock.isRunning());
		
		if(exportPanel != null) {
			sd.putValue(ScribePluginSaveData.EXPORT_FILE_KEY, 
					exportPanel.getLastExportFile());
			sd.putValue(ScribePluginSaveData.EXPORT_SIGNATURE_KEY, 
					exportPanel.isCreateSignatureFieldsSelected());
			sd.putValue(ScribePluginSaveData.EXPORT_MEETINGS_KEY, 
					exportPanel.isExportOfAllMeetingsSelected());
			sd.putValue(ScribePluginSaveData.EXPORT_IMAGES_KEY, 
					exportPanel.isPrintImagesSelected());
		}
		
		
		return sd;
	}
	
	private void validateReviewFormOnSave() {
		validateReviewForm();
		
		String message = getLocalizedString("error.save.validation");
		String title = getLocalizedString("error.save.validation.title");
		pluginAdapter.getSpringGlassPane().setFaded(true);
		JOptionPane.showMessageDialog(this.mainScrollPane, 
					message, 
					title, 
					JOptionPane.ERROR_MESSAGE);
		pluginAdapter.getSpringGlassPane().setFaded(false);
	}
	
	@Override
	public void validateReviewForm() {
		for (int i=0; i<views.length; i++) {
			checkTab(i);
		}
	}
	
	@Override
	public boolean isValidReviewForm() {
		return isProtocolValid();
	}
	
	@Override
	public boolean isMeetingValid() {
		if(meeting == null)
			return true;
		boolean isCanceled = exportPanel.getCanceled() != null;
		
		return isCanceled ? true : isProtocolValid();
	}
	
	@Override
	public boolean isProtocolValid() {
		if(meeting == null)
			return true;
		for (int i=0; i<views.length; i++) {
			ScribePanel panel = (ScribePanel)views[i];
			if(!panel.isValidForm())
				return false;
		}
		
		return true;
	}
	
	/**
	 * Save the review. if review was not saved before, 
	 * method <code>SaveAsReview</code> will be called
	 * 
	 * @return <code>true</code> if review was successful saved
	 */
	public boolean saveReview() {
		if(!isMeetingValid()) {
			validateReviewFormOnSave();
			return false;
		}
		
		if(reviewFile == null)
			return saveAsReview();
		else
			return writeReview(reviewFile);
	}
	
	/**
	 * Show save under dialog, and save the current review to selected file
	 * by using method <code>writeReview</code>.
	 * @return <code>true</code> if review was saved, 
	 * otherwise <code>false</code>
	 */
	public boolean saveAsReview() {
		if(!isMeetingValid()) {
			validateReviewFormOnSave();
			return false;
		}
		
		final JFileChooser saveDialog = new JFileChooser();
		saveDialog.setMultiSelectionEnabled(false);
		saveDialog.setAcceptAllFileFilterUsed(false);
		
		XMLFileFilter reviewFilter = new XMLFileFilter();
		saveDialog.setFileFilter(reviewFilter);
		
		saveDialog.setCurrentDirectory(reviewFile);
		saveDialog.setSelectedFile(
				reviewFile == null ? getDefaultSaveAsFile() : reviewFile);
		
		pluginAdapter.getSpringGlassPane().setFaded(true);
		int ret = saveDialog.showSaveDialog(mainScrollPane);
		pluginAdapter.getSpringGlassPane().setFaded(false);
			 
		if (JFileChooser.APPROVE_OPTION != ret)
			return false;
		
		File selectedFile = saveDialog.getSelectedFile();
		if(!reviewFilter.accept(selectedFile)) {
			String fileName = selectedFile.getName();
			fileName += XMLFileFilter.EXTENSION;
			selectedFile = new File(selectedFile.getParent(), fileName);
		}
		return writeReview(selectedFile);
	}
	
	private int getHighestFindingId(List<Meeting> meetings) {
		int id = 0;
		
		for(Meeting meeting: meetings) {
			Protocol protocol = meeting.getProtocol();
			if(protocol == null)
				continue;
			
			List<Finding> findings = protocol.getFindings();
			if(findings == null)
				continue;
			
			for(Finding finding: findings) {
				try{
					id = Math.max(id, Integer.parseInt(finding.getId()));
				} catch(NumberFormatException e) {
					// ignore
				}
			}
		}
		
		return id;
	}
	
	private void setProtocol(Protocol protocol) {
		attendeePanel.setAttendees(review.getAttendees(),
				protocol.getAvailableAttendees());
		
		informationPanel.setProtocol(protocol);
		
		int id = getHighestFindingId(review.getMeetings());
		findingsPanel.setFindings(protocol.getFindings(), id);
		
		exportPanel.setComments(protocol.getComments());
		
		if(meetingRunning)
			return;
			
		long passed = ScribeClock.getMillisecondsDifference(protocol.getStart(), 
				protocol.getEnd());
		clock.setPassedTime(passed);
		clock.setEnabled(false);
	}
	
	private void setMeeting(Meeting m) {
		meeting = m;
		
		for(int i = 1 ; i < getSizeView() ; i++)
			stepTabs.setTabUnchecked(i);
		
		meetingsPanel.setSelectedMeeting(meeting);
		
		informationPanel = new InformationPanel();
		attendeePanel = new AttendeePanel();
		findingsPanel = new FindingsPanel(pluginAdapter.getSpringGlassPane());
		exportPanel = new ExportPanel(pluginAdapter.getSpringGlassPane());
		
		exportPanel.setReviewValidator(this);
		
		views = new Component[]{startPanel, meetingsPanel, informationPanel,
				attendeePanel, findingsPanel, exportPanel};
		
		clock.stopTimer();
		clock.setStartEndTime(meeting.getPlannedStart(), 
				meeting.getPlannedEnd());
		
		if(meeting.getProtocol() == null) {
			meetingRunning = true;
			clock.startTimer();
			clock.setEnabled(true);
			
			attendeePanel.setAttendees(review.getAttendees());
			
			informationPanel.setMeeting(meeting);
			
			meeting.setProtocol(new Protocol());
			meeting.getProtocol().setEnd(meeting.getPlannedEnd());
		} else {
			informationPanel.setMeetingComments(meeting.getComments());
			Protocol protocol = meeting.getProtocol();
			setProtocol(protocol);
		}
		
		attendeePanel.setImpression(review.getImpression());
		
		findingsPanel.setSeverities(review.getSeverities());
		findingsPanel.setAspects(review.getAspects());
		
		exportPanel.setCanceled(meeting.getCanceled());
		exportPanel.setRecommendation(review.getRecommendation());
		
		stepTabs.setMaxSelectionIndex(-1);
		setNextView();
		
		saveReviewAction.setEnabled(true);
		saveAsReviewAction.setEnabled(true);
		
		setActionsToPanels();
	}
	
	/**
	 * Sets the actions like nextView or previousView to the panels.
	 */
	private void setActionsToPanels() {
		for (int i=0; i<views.length; i++) {
			ScribePanel panel = (ScribePanel)views[i];
			panel.setNextViewAction(nextViewAction);
			panel.setPreviousViewAction(previousViewAction);
		}
	}
	
	public void setReview(Review r) {
		review = r;
		
		meetingsPanel = new MeetingsPanel();
		meetingsPanel.setMeetingSelectionActionListener(
				new MeetingSelectedAction());
		meetingsPanel.setReview(r);

		views = new Component[]{startPanel, meetingsPanel};
		
		if(r == null)
			return;
		
		meetingsPanel.setMeetings(r.getMeetings());
	}
	
	public Review getReview() {
		return getReview(false);
	}
	
	/**
	 * Returns current review
	 * @param withValidProtocol review will be returned with:<br><br>
	 * 
	 * isProtocolValid() = true, review with protocol will be returned <br><br>
	 * 
	 * isProtocolValid() = false && withValidProtocol = true 
	 * && isMeetingValid() = true,
	 * a review without protocol will be returned<br><br>
	 * 
	 * others returns review with invalid protocol
	 * 
	 * @return review
	 */
	public Review getReview(boolean withValidProtocol) {
		if(meeting == null)
			return review;
		
		review.setSeverities(findingsPanel.getSeverities());
		review.setImpression(attendeePanel.getImpression());
		review.setRecommendation(exportPanel.getRecommendation());
		review.setAttendees(attendeePanel.getAttendees());
		
		meeting.setComments(informationPanel.getMeetingComments());
			
		meeting.setCanceled(exportPanel.getCanceled());
		
		
		Protocol protocol;
		if(meeting.getProtocol() == null)
			protocol = new Protocol();
		else
			protocol = meeting.getProtocol();
			
		informationPanel.getProtocol(protocol);
			
		protocol.setAvailableAttendees(
					attendeePanel.getAvailableAttendees());
		protocol.setFindings(findingsPanel.getFindings());
		
		protocol.setComments(exportPanel.getComments());
		
		Calendar c = (Calendar)protocol.getStart().clone();
		c.setTimeInMillis(c.getTimeInMillis() + clock.getPassedTime());
		protocol.setEnd(c);
			
		meeting.setProtocol(protocol);
		
		if(!isProtocolValid() && isMeetingValid() && withValidProtocol == true){
			meeting.setProtocol(null);
		}
		
		return review;
	}
	
	/**
	 * Write review to file and shows error message if fails
	 * @param f write to this file
	 * @return <code>true</code> if no errors occurred, 
	 * otherwise <code>false</code>
	 */
	private boolean writeReview(File f) {
		try {
			pluginAdapter.getFileHistory().add(f, FILE_HISTORY_TYPE);
			ReviewWriter writer = new XMLReviewWriter(XML_PREFIX);
			Review r = getReview(true);
			writer.write(r, f);
			reviewFile = f;
			pluginAdapter.setTitle(reviewFile.getName(), 
					reviewFile.getAbsolutePath());
			pluginAdapter.setFile(reviewFile);
			return true;
		} catch(Throwable e) {
			String message = getLocalizedString("error.save.exception");
			String title = getLocalizedString("error.save.exception.title");
			Formatter formatter = new Formatter();
			formatter.format(message, e.getLocalizedMessage());
			pluginAdapter.getSpringGlassPane().setFaded(true);
			JOptionPane.showMessageDialog(this.mainScrollPane, 
						formatter.toString(), 
						title, 
						JOptionPane.ERROR_MESSAGE);
			pluginAdapter.getSpringGlassPane().setFaded(false);
		}
		
		return false;
	}
	
	public boolean readReview(File f) {
		try {
			ReviewReader reader = new XMLReviewReader();
			Review review = reader.read(f);
			pluginAdapter.getFileHistory().add(f, FILE_HISTORY_TYPE);
			setReview(review);
			setStartMode(false);
			setNextView();
			reviewFile = f;
			pluginAdapter.setTitle(reviewFile.getName(), 
					reviewFile.getAbsolutePath());
			pluginAdapter.setFile(reviewFile);
			return true;
		} catch(Throwable e) {
			String message = getLocalizedString("error.open.exception");
			String title = getLocalizedString("error.open.exception.title");
			Formatter formatter = new Formatter();
			formatter.format(message, e.getLocalizedMessage());
			pluginAdapter.getSpringGlassPane().setFaded(true);
			JOptionPane.showMessageDialog(this.mainScrollPane, 
						formatter.toString(), 
						title, 
						JOptionPane.ERROR_MESSAGE);
			pluginAdapter.getSpringGlassPane().setFaded(false);
		}
		
		return false;
	}
	
	public void openReview(File f) {
		if(!startMode) {
				ScribePlugin plugin = new ScribePlugin();
				plugin.openReviewOnRun(f);
				pluginAdapter.newPluginTab(plugin, true);
		} else {
			readReview(f);
		}
	}
	
	public void openReview() {
		final JFileChooser openDialog = new JFileChooser();
		openDialog.setMultiSelectionEnabled(false);
		
		openDialog.setFileFilter(new XMLFileFilter());
		
		pluginAdapter.getSpringGlassPane().setFaded(true);
		int ret = openDialog.showOpenDialog(mainScrollPane);
		pluginAdapter.getSpringGlassPane().setFaded(false);
			 
		if (JFileChooser.APPROVE_OPTION != ret)
			return;
				 
		File f = openDialog.getSelectedFile();
				 
		if (f.isFile() && f.canRead()) {
			openReview(f);
		}
	}
	
	public boolean close() {
		if(!startMode) {
			String message = getLocalizedString("confirm.close");
			Formatter formatter = new Formatter();
			if(reviewFile == null)
				message = formatter.format(message, 
						getLocalizedString("start.new_file")).toString();
			else
				message = formatter.format(message, 
						reviewFile.getName()).toString();
			
			int option = JOptionPane.showConfirmDialog(mainScrollPane, 
					message, 
					getLocalizedString("confirm.close.title"), 
					JOptionPane.YES_NO_CANCEL_OPTION);
			
			switch(option) {
			case JOptionPane.CANCEL_OPTION:
				return false;
				
			case JOptionPane.NO_OPTION:
				return true;
				
			case JOptionPane.YES_OPTION:
				return saveReview();
			}
		}
		
		return true;
	}

	private class TabListener implements ListSelectionListener, Serializable {
		private static final long serialVersionUID = 2799594503654054503L;
		@Override
		public void valueChanged(ListSelectionEvent e) {
			updateView();
		}
	}
	
	private class NavigateToHomeAction extends AbstractAction {
		private static final long serialVersionUID = 6937468520337445529L;
		public NavigateToHomeAction() {
			putValue(Action.NAME, getLocalizedString("menu.home"));
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			if(close()) {
				pluginAdapter.navigateToStartTab();
			}
		}
	}
	
	public class NextViewAction extends AbstractAction {
		private static final long serialVersionUID = -5051186228771979304L;
		@Override
		public void actionPerformed(ActionEvent e) {
			setNextView();
		}
	}
	
	public class PreviousViewAction extends AbstractAction {
		private static final long serialVersionUID = -5252028168971002171L;
		@Override
		public void actionPerformed(ActionEvent e) {
			setPreviousView();
		}
	}
	
	public class FirstViewAction extends AbstractAction {
		private static final long serialVersionUID = -6038555634600549846L;
		@Override
		public void actionPerformed(ActionEvent e) {
			setView(0);
		}
	}
	
	public class MeetingSelectedAction implements ActionListener, 
			Serializable {
		private static final long serialVersionUID = 3563249554725861176L;
		@Override
		public void actionPerformed(ActionEvent e) {
			if(meeting == null) {
				meetingRunning = false;
				Meeting m = meetingsPanel.getSelectedMeeting();
				meetingsPanel.setHighlightMeeting(m);
				setMeeting(m);
			} else if(meetingRunning) {
				int option = JOptionPane.showConfirmDialog(mainScrollPane, 
						getLocalizedString("confirm.choose"), 
						getLocalizedString("confirm.choose.title"), 
						JOptionPane.YES_NO_CANCEL_OPTION);
				switch(option) {
				/* apply protocol and start new meeting */
				case JOptionPane.YES_OPTION:
					if(isValidReviewForm()) {
						getReview();
						meetingRunning = false;
						Meeting m = meetingsPanel.getSelectedMeeting();
						meetingsPanel.setHighlightMeeting(m);
						setMeeting(m);
					}
					else
						validateReviewForm();
					break;
				/* not apply protocol and start new meeting */
				case JOptionPane.NO_OPTION:
					meeting.setProtocol(null);
					meetingRunning = false;
					Meeting m = meetingsPanel.getSelectedMeeting();
					meetingsPanel.setHighlightMeeting(m);
					setMeeting(m);
					break;
				/* operation canceled */
				case JOptionPane.CANCEL_OPTION:
					break;
				}
			}
			else if(isValidReviewForm()) {
				getReview();
				meetingRunning = false;
				Meeting m = meetingsPanel.getSelectedMeeting();
				meetingsPanel.setHighlightMeeting(m);
				setMeeting(m);
			} else {
				int option = JOptionPane.showConfirmDialog(mainScrollPane, 
						getLocalizedString("confirm.choose.invalid"), 
						getLocalizedString("confirm.choose.title"), 
						JOptionPane.YES_NO_OPTION);
				switch(option) {
				/* do not apply protocol */
				case JOptionPane.YES_OPTION:
					meeting.setProtocol(null);
					meetingRunning = false;
					Meeting m = meetingsPanel.getSelectedMeeting();
					meetingsPanel.setHighlightMeeting(m);
					setMeeting(m);
					break;
				/* validate forms, and do nothing */
				case JOptionPane.NO_OPTION:
					meetingsPanel.setSelectedMeeting(meeting);
					validateReviewForm();
					break;
				}
			}
		}
	}
	
	private class OpenReviewAction extends AbstractAction {
		private static final long serialVersionUID = -1349974230460227875L;
		public OpenReviewAction() {
			putValue(Action.NAME, getLocalizedString("menu.open"));
			
			KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_O, 
					Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
			putValue(Action.ACCELERATOR_KEY, ks);
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			openReview();
		}
	}
	
	private class OpenReviewFileAction extends AbstractAction {
		private static final long serialVersionUID = 7380782108079956873L;
		@Override
		public void actionPerformed(ActionEvent e) {
			openReview(startPanel.getHistoryFile());
		}
	}
	
	private class SaveReviewAction extends AbstractAction {
		private static final long serialVersionUID = -6895080742593347857L;
		public SaveReviewAction() {
			setEnabled(false);
			
			putValue(Action.NAME, getLocalizedString("menu.save"));
			
			KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_S, 
					Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
			putValue(Action.ACCELERATOR_KEY, ks);
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			saveReview();
		}		
	}
	
	private class SaveAsReviewAction extends AbstractAction {
		private static final long serialVersionUID = -8493350029946035608L;
		public SaveAsReviewAction() {
			setEnabled(false);
			
			putValue(Action.NAME, getLocalizedString("menu.save_as"));
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			saveAsReview();
		}
	}
}